using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Gma.QrCodeNet.Encoding;

internal sealed class BitList : IEnumerable<bool>
{
	internal BitList()
	{
		Count = 0;
		List = new List<byte>(32);
	}

	internal BitList(IEnumerable<byte> byteArray)
	{
		Count = byteArray.Count();
		List = byteArray.ToList();
	}

	internal List<byte> List { get; }

	internal int Count { get; private set; }

	internal bool this[int index]
	{
		get
		{
			if (index < 0 || index >= Count)
			{
				throw new ArgumentOutOfRangeException(nameof(index), "Index out of range");
			}

			int value_Renamed = List[index >> 3] & 0xff;
			return ((value_Renamed >> (7 - (index & 0x7))) & 1) == 1;
		}
	}

	public IEnumerator<bool> GetEnumerator()
	{
		int numBytes = Count >> 3;
		int remainder = Count & 0x7;
		byte value;
		for (int index = 0; index < numBytes; index++)
		{
			value = List[index];
			for (int shiftNum = 7; shiftNum >= 0; shiftNum--)
			{
				yield return ((value >> shiftNum) & 1) == 1;
			}
		}
		if (remainder > 0)
		{
			value = List[numBytes];
			for (int index = 0; index < remainder; index++)
			{
				yield return ((value >> (7 - index)) & 1) == 1;
			}
		}
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return GetEnumerator();
	}

	private int ToBit(bool item)
	{
		return item ? 1 : 0;
	}

	internal void Add(bool item)
	{
		int numBitsinLastByte = Count & 0x7;

		// Add one more byte to List when we have no bits in the last byte.
		if (numBitsinLastByte == 0)
		{
			List.Add(0);
		}

		List[Count >> 3] |= (byte)(ToBit(item) << (7 - numBitsinLastByte));
		Count++;
	}

	internal void Add(IEnumerable<bool> items)
	{
		foreach (bool item in items)
		{
			Add(item);
		}
	}

	internal void Add(int value, int bitCount)
	{
		if (bitCount is < 0 or > 32)
		{
			throw new ArgumentOutOfRangeException(nameof(bitCount), $"{nameof(bitCount)} must be greater than or equal to 0");
		}

		int numBitsLeft = bitCount;

		while (numBitsLeft > 0)
		{
			if ((Count & 0x7) == 0 && numBitsLeft >= 8)
			{
				// Add one more byte to List.
				byte newByte = (byte)((value >> (numBitsLeft - 8)) & 0xFF);
				AppendByte(newByte);
				numBitsLeft -= 8;
			}
			else
			{
				bool bit = ((value >> (numBitsLeft - 1)) & 1) == 1;
				Add(bit);
				numBitsLeft--;
			}
		}
	}

	private void AppendByte(byte item)
	{
		List.Add(item);
		Count += 8;
	}
}
